package com.fowo.api.common.model;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.fowo.api.common.lang.ExceptionRunnable;
import com.fowo.api.common.messages.MessageKeys;
import com.fowo.api.common.messages.MessageUtils;
import com.fowo.api.sys.component.ExecuteScriptEngine;
import lombok.Data;

import java.io.Serializable;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * 通用 API 返回值包装
 */
@Data
public class R<T> implements Serializable {

    /**
     * 是否成功
     */
    private boolean success;
    /**
     * 返回消息
     */
    private String message;
    /**
     * 错误消息
     */
    private String errorMessage;
    /**
     * 用户消息
     */
    private List<String> userMessage;
    /**
     * 数据
     */
    private T data;
    /**
     * 附加信息
     */
    private Object attached;

    /**
     * 获取用户设置信息
     *
     * @return
     */
    private static List<String> buildUserMessage() {
        try {
            ExecuteScriptEngine engine = SpringUtil.getBean(ExecuteScriptEngine.class);
            return ObjectUtil.isNotEmpty(engine) ? CollUtil.popPart(engine.getUserMessage(), CollUtil.size(engine.getUserMessage())) : null;
        } catch (Exception ex) {
            return null;
        }
    }


    /**
     * 返回失败结果
     *
     * @param message 失败消息
     */
    public static <T> R<T> fail(String message) {
        R r = new R();
        r.success = false;
        r.errorMessage = message;
        r.userMessage = buildUserMessage();
        return r;
    }

    /**
     * 返回失败结果
     *
     * @param message 失败消息
     */
    public static <T> R<T> fail(String message, T data) {
        R r = new R();
        r.success = false;
        r.errorMessage = message;
        r.data = data;
        r.userMessage = buildUserMessage();
        return r;
    }

    /**
     * 返回成功结果
     *
     * @param message 失败消息
     */
    public static <T> R<T> success(String message, T data) {
        R<T> r = new R<T>();
        r.success = true;
        r.message = message;
        r.data = data;
        r.userMessage = buildUserMessage();
        return r;
    }

    /**
     * 直接返回操作成功或失败
     *
     * @param isSuccess 操作是否成功
     */
    public static R ok(boolean isSuccess) {
        return isSuccess ? R.success(MessageUtils.getOrDefault(MessageKeys.R_OK, "操作成功"), null) : R.fail(MessageUtils.getOrDefault(MessageKeys.R_FAIL, "操作失败"));
    }

    public static R ok(Callable<Boolean> callable) {
        try {
            return R.ok(callable.call());
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail(e.getLocalizedMessage());
        }
    }

    /**
     * 执行操作，返回操作是否成功
     *
     * @param runnable 要执行的操作
     */
    public static R run(ExceptionRunnable runnable) {
        try {
            runnable.run();
            return R.success(MessageUtils.getOrDefault(MessageKeys.R_OK, "操作成功"), null);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail(e.getLocalizedMessage());
        }
    }

    /**
     * 返回数据是否成功，data 空为失败
     *
     * @param data 要返回的数据
     * @param <T>  数据类型
     */
    public static <T> R<T> data(T data) {
        if (data == null) {
            return R.fail(MessageUtils.getOrDefault(MessageKeys.R_NO_DATA, "未找到此数据"));
        }
        if (data instanceof R) {
            //noinspection unchecked
            return (R<T>) data;
        }
        return R.success("读取成功", data);
    }

    /**
     * 返回数据是否成功，data 空为失败
     *
     * @param data    要返回的数据
     * @param message 要返回的消息
     * @param <T>     数据类型
     */
    public static <T> R<T> data(T data, String message) {
        if (data == null) {
            return R.fail(MessageUtils.getOrDefault(MessageKeys.R_NO_DATA, "未找到此数据"));
        }
        return R.success(message, data);
    }

    /**
     * 执行数据查询，返回非空成功，其它失败
     *
     * @param callable 数据查询方法
     * @param <T>      数据类型
     */
    public static <T> R<T> data(Callable<T> callable) {
        try {
            return R.data(callable.call());
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail(e.getLocalizedMessage());
        }
    }

    /**
     * 执行数据查询，返回非空成功，其它失败
     *
     * @param callable 数据查询方法
     * @param <T>      数据类型
     */
    public static <T> R<T> data(Callable<T> callable, Object attached) {
        R<T> res = null;
        try {
            res = R.data(callable.call());
        } catch (Exception e) {
            e.printStackTrace();
            res = R.fail(e.getLocalizedMessage());
        } finally {
            res.setAttached(attached);
            return res;
        }
    }
}
